#!/bin/bash
#============================================================================
# ${XEN_SCRIPT_DIR}/vif-nat
#
# Script for configuring a vif in routed-nat mode.
# The hotplugging system will call this script if it is specified either in
# the device configuration given to Xend, or the default Xend configuration
# in ${XEN_CONFIG_DIR}/xend-config.sxp.  If the script is specified in
# neither of those places, then vif-bridge is the default.
#
# Usage:
# vif-nat (add|remove|online|offline)
#
# Environment vars:
# dev         vif interface name (required).
# XENBUS_PATH path to this device's details in the XenStore (required).
#
# Parameters:
# dhcp        Whether to alter the local DHCP configuration to include this
#             new host (default no).
#
# Read from the store:
# ip      list of IP networks for the vif, space-separated (default given in
#         this script).
#============================================================================


dir=$(dirname "$0")
. "$dir/vif-common.sh"

# turn on dhcp feature by default if dhcpd is installed
if [ -f /etc/dhcpd.conf ]
then
	dhcp=${dhcp:-yes}
else
	dhcp=${dhcp:-no}
fi

if [ "$dhcp" != 'no' ]
then
  dhcpd_conf_file=$(find_dhcpd_conf_file)
  dhcpd_init_file=$(find_dhcpd_init_file)
  dhcpd_arg_file=$(find_dhcpd_arg_file)
  if [ -z "$dhcpd_conf_file" ] || [ -z "$dhcpd_init_file" ] || [ -z "$dhcpd_arg_file" ]
  then
    echo 'Failed to find dhcpd configuration or init or args file.' >&2
    exit 1
  fi
fi


domid=$(xenstore_read "$XENBUS_PATH/frontend-id")
vifid=$(xenstore_read "$XENBUS_PATH/handle")
vifid=$(( $vifid + 1 ))


ip_from_dom()
{
  local domid1=$(( $domid / 256 ))
  local domid2=$(( $domid % 256 ))

  echo "10.$domid1.$domid2.$vifid/16"
}


routing_ip()
{
  echo $(echo $1 | awk -F. '{print $1"."$2"."$3"."$4 + 127}')
}


dotted_quad()
{
 echo\
 $(( ($1 & 0xFF000000) >> 24))\
.$(( ($1 & 0x00FF0000) >> 16))\
.$(( ($1 & 0x0000FF00) >> 8 ))\
.$((  $1 & 0x000000FF       ))
}


if [ "$ip" = "" ]
then
  ip=$(ip_from_dom)
fi

router_ip=$(routing_ip "$ip")

# Split the given IP/bits pair.
vif_ip=`echo ${ip} | awk -F/ '{print $1}'`

hostname=$(xenstore_read "$XENBUS_PATH/domain" | tr -- '_.:/+' '-----')
if [ "$vifid" != "1" ]
then
  hostname="$hostname-$vifid"
fi

dhcparg_remove_entry()
{
  local tmpfile=$(mktemp)
  sed -e "s/${dev} //" "$dhcpd_arg_file" >"$tmpfile"
  if diff "$tmpfile" "$dhcpd_arg_file" >/dev/null
  then
    rm "$tmpfile"
  else
    mv "$tmpfile" "$dhcpd_arg_file"
  fi
}

dhcparg_add_entry()
{
  dhcparg_remove_entry
  local tmpfile=$(mktemp)
  # handle Red Hat, SUSE, and Debian styles, with or without quotes
  sed -e 's/^DHCPDARGS="*\([^"]*\)"*/DHCPDARGS="\1'"${dev} "'"/' \
     "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file"
  sed -e 's/^DHCPD_INTERFACE="*\([^"]*\)"*/DHCPD_INTERFACE="\1'"${dev} "'"/' \
     "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file"
  sed -e 's/^INTERFACES="*\([^"]*\)"*/INTERFACES="\1'"${dev} "'"/' \
     "$dhcpd_arg_file" >"$tmpfile" && mv "$tmpfile" "$dhcpd_arg_file"
  rm -f "$tmpfile"
}

dhcp_remove_entry()
{
  local tmpfile=$(mktemp)
  grep -v "host $hostname" "$dhcpd_conf_file" >"$tmpfile"
  if diff "$tmpfile" "$dhcpd_conf_file" >/dev/null
  then
    rm "$tmpfile"
  else
    mv "$tmpfile" "$dhcpd_conf_file"
  fi
  dhcparg_remove_entry
}


dhcp_up()
{
  claim_lock "vif-nat-dhcp"
  dhcp_remove_entry
  mac=$(xenstore_read "$XENBUS_PATH/mac")
  echo >>"$dhcpd_conf_file" \
"host $hostname { hardware ethernet $mac; fixed-address $vif_ip; option routers $router_ip; option host-name \"$hostname\"; }"
  dhcparg_add_entry
  release_lock "vif-nat-dhcp"
  "$dhcpd_init_file" restart || true
}


dhcp_down()
{
  claim_lock "vif-nat-dhcp"
  dhcp_remove_entry
  release_lock "vif-nat-dhcp"
  "$dhcpd_init_file" restart || true # We need to ignore failure because
                                     # ISC dhcpd 3 borks if there is nothing
                                     # for it to do, which is the case if
                                     # the outgoing interface is not
                                     # configured to offer leases and there
                                     # are no vifs.
}


case "$command" in
    online)
        if ip route | grep -q "dev ${dev}"
        then
          log debug "${dev} already up"
          exit 0
        fi

        do_or_die ip link set dev "${dev}" up arp on
        do_or_die ip addr add "$router_ip" dev "${dev}"
        do_or_die ip route add "$vif_ip" dev "${dev}" src "$router_ip"
        echo 1 >/proc/sys/net/ipv4/conf/${dev}/proxy_arp
        [ "$dhcp" != 'no' ] && dhcp_up
        ;;
    offline)
        [ "$dhcp" != 'no' ] && dhcp_down
        do_without_error ifconfig "${dev}" down
        ;;
esac


handle_iptable

call_hooks vif post

log debug "Successful vif-nat $command for ${dev}."
if [ "$command" = "online" ]
then
  success
fi
